﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
//类和对象中有四个特殊函数，此程序旨在研究学习其中的构造函数
//构造函数

//构造函数是特殊的成员函数，需要注意的是，构造函数虽然名称叫构造，
//但是构造函数的主要任务并不是开空间创建对象(我们常使用的局部对象是栈帧创建时，空间就开好了)
//而是对象实例化时初始化对象。构造函数的本质是要替代我们以前Stack和Date类中写的Init函数的功能，
//构造函数自动调用的特点就完美的替代的了Init。
// 
//构造函数的特点：
//1.函数名与类名相同。
//2.无返回值。(返回值啥都不需要给，也不需要写void，不要纠结，C++规定如此)
//3.对象实例化时系统会自动调用对应的构造函数。
//4.构造函数可以重载。
//5.如果类中没有显式定义构造函数，则C++编译器会自动生成一个无参的默认构造函数，一旦用户显式定义编译器将不再生成。
//6.无参构造函数、全缺省构造函数、我们不写构造时编译器默认生成的构造函数，都叫做默认构造函数。但是这三个函数有且只有一个存在，
//  不能同时存在。无参构造函数和全缺省构造函数虽然构成函数重载，但是调用时会存在歧义。
//  要注意很多同学会认为默认构造函数是编译器默认生成那个叫默认构造，实际上无参构造函数、
//  全缺省构造函数也是默认构造，总结一下就是不传实参就可以调用的构造就叫默认构造。
//7.我们不写，编译器默认生成的构造，对内置类型成员变量的初始化没有要求，
//  也就是说是是否初始化是不确定的，看编译器。对于自定义类型成员变量，
//  要求调用这个成员变量的默认构造函数初始化。如果这个成员变量，没有默认构造函数，
//  那么就会报错，我们要初始化这个成员变量，需要用初始化列表才能解决

//注：编译器自动生成的，自己写的无参的构造函数，全缺省构造函数三者都称为默认构造函数
//    而由于只要写了构造函数，无论是否有参（也就是说可以包括必须传参的函数），就不会
//    自动生成了，而全缺省和无参由于函数重载的原因，会造成歧义，因此，三者只可存一
//	  但由于构造函数也可以是必须传参的函数，所以，默认构造函数其实不一定存在
//	  *默认构造函数也不一定要写在公有中，主要看该函数需要在什么时候调用来确定书写的位置*
// 
//    对于内置类型变量：允许出现默认构造函数不存在的场景；
// 
//    而对于自定义变量：
//		 关于默认构造与报错的关系​​：
//    只要自定义类型成员b存在默认构造函数（无论是编译器自动生成、用户定义的无参构造，还是全缺省构造），使用时不会报错。
//    若自定义类型成员b完全没有默认构造（只有必须传参的构造函数），且包含它的类使用编译器生成的默认构造时，必定报错。
//	     初始化列表的解决方案：
//    即使自定义类型成员b没有默认构造，只要在包含它的类的构造函数中，通过初始化列表显式调用带参构造，即可规避报错


//默认构造函数成员为内置类型举例
class dzh1
{
public:
	//先让编译器自己写一个，看看效果如何

	/*自己写一个默认构造函数（无参）
	dzh1()
	{
		_x = 0;
		_y = 0;
	}*/

	/*再写一个全缺省默认构造函数
	dzh1(int x = 1, int y = 1)
	{
		_x = x;
		_y = y;
	}*/

	//再写一个必须传参的构造函数，看看效果
	dzh1(int x, int y)
	{
		_x = x;
		_y = y;
	}
	void Print()
	{
		std::cout << _x << " " << _y << std::endl;
	}
private:
	int _x;
	int _y;
};

void test1()
{
	//对于默认构造函数的使用：
	//dzh1 a;//->创建对象时自动调用默认构造函数
	//a.Print();//-858993460 -858993460\n ->编译器自动写默认构造函数的结果，可以看到，仍是随机值
	//a.Print();//0 0\n ->无参默认构造函数结果
	//a.Print();//1 1\n

	//对于必须传参的构造函数的使用
	dzh1 b(2, 2);//若使用需传参的构造函数，则必须在创建对象时传入参数
	b.Print();//2 2\n
}



//默认构造函数成员为自定义类型举例
//
//首先来理解一下“若自定义类型成员完全没有默认构造（只有必须传参的构造函数），且包含它的类使用编译器生成的默认构造时，必定报错。”
//这句话，“自定义类型成员”就是说在一个类a中，它的成员存在自定义类型b；“完全没有默认构造”指的是a没有默认构造；
//“且包含它的类使用编译器生成的默认构造”说的是b使用了自动生成的默认构造

//梳理好之后，先写自定义类型b（这里以Stack举例）：
class Stack
{
public:
	//设置了全缺省默认构造函数
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int*) * capacity);
		_capacity = capacity;
		_size = capacity;
	}

	void Print()
	{
		std::cout << _capacity << std::endl;
	}
private:
	int* _a;
	int _size;
	int _capacity;
};

//再写a
class Queue
{
public:
	//使用编译器自动生成的默认构造函数

	void Print()
	{
		_a.Print();
		_b.Print();
	}
private:
	Stack _a;//这便是构造函数成员为自定义类型
	Stack _b;
};
void test2()
{
	//根据上述规则，若类a中含有自定义类型，且自定义类型的类b中包含非编译器自动生成的默认构造函数时
	//使用a中自动生成的默认构造函数来达成初始化 ->实验结果：正确
	Queue a;
	a.Print();//4\n4
}

//若类中含自定义成员变量，当显式写出构造函数时，编译器会自动调用自定义成员的默认构造函数
class A
{
public:
	//默认构造函数用以初始化_a
	A()
	{
		_a = 1000;
	}

	//观察_a的print函数
	void print()
	{
		std::cout << _a << std::endl;
 	}
private:
	int _a;
};
class B
{
public:
	//显式写出B的构造函数
	B()
	{
		_num = 99;
	}
	//print函数验证_s是否自动调用了其默认构造
	void print()
	{
		_s.print();
		std::cout << _num << std::endl;
	}
private:
	int _num = 0;
	A _s;
};
void test3()
{
	B s;
	s.print();//1000\n99\n->自动调用了A的默认构造进行初始化
}
int main()
{
	//test1();
	//test2();
	test3();
	return 0;
}

